package apobates.gui.formatter;

import apobates.gui.formatter.packet.JsonResult;
import apobates.gui.formatter.packet.ResourceType;
import apobates.gui.formatter.reader.RemoteForm;
import apobates.gui.formatter.richedit.CodeAreaHelper;
import apobates.gui.formatter.richedit.SourceAreaHelper;
import apobates.gui.formatter.service.JsonFileService;
import apobates.gui.formatter.service.RecentExecutorService;
import apobates.gui.formatter.service.SourceContentRender;
import apobates.gui.formatter.storage.RecentRecord;
import apobates.gui.formatter.tree.JsonNodeTreeGsonBuilder;
import apobates.gui.formatter.tree.JsonNodeValueType;
import apobates.gui.formatter.util.Core;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.InlineCssTextArea;
import org.reactfx.Change;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * JavaFx GUI 主控制器
 * @since 20230227
 */
public class JsonController {
    @FXML
    private SplitPane contentBody;
    // 左侧的：输入源
    @FXML
    private AnchorPane leftPane;
    // 右侧的: 展示区
    @FXML
    private AnchorPane rightPane;
    // 底部: 统计区
    @FXML
    private AnchorPane bottomPane;
    // 清除按钮
    @FXML
    private Button rightActionBtn;
    // 格式化按钮
    @FXML
    private Button leftActionBtn;
    // 输入源的控件
    @FXML
    private InlineCssTextArea leftSource;
    // 展示区的控件
    @FXML
    private CodeArea rightView;
    @FXML
    private ComboBox fontSizeSelect;
    @FXML
    private ComboBox fontFamilySelect;
    @FXML
    private ColorPicker fontColorSelect;
    // 当前stage
    @FXML
    private VBox rootLayout;
    // 菜单
    @FXML
    private ToolBar headerMenu;
    @FXML
    private MenuItem menuOpenBtn;
    @FXML
    private MenuItem menuSaveBtn;
    @FXML
    private MenuItem menuViewBtn;
    @FXML
    private MenuItem menuExistBtn;
    @FXML
    private Menu menuRecentBtn;
    // 底部的统计显示区
    // JSON类型统计
    @FXML
    private Label jsonTypeGroup;
    // JSON内容长度
    @FXML
    private Label strCountLabel;
    // JSON来源的资源类型
    @FXML
    private Label fileType;
    // 待格式的JSON内容信息
    private JsonResult jsonResult;
    // 右侧的编辑器助手类
    private CodeAreaHelper editorHelper;
    // 右侧的编辑器内容属性类
    private SimpleStringProperty richEditorContent = new SimpleStringProperty();
    // 最近记录执行器
    private RecentExecutorService recentExecutorService;
    // JSON内容前置检查
    private static Predicate<String> isBlankContent = (content)->null == content || content.isEmpty();
    // 最近记录加载状态消费函数
    private Consumer<Boolean> recentLoadingFun = (loaded)->{
        if(!loaded){
            jsonTypeGroup.setText("加载中...");
        }else{
            jsonTypeGroup.setText("");
        }
    };
    @FXML
    public void initialize(){
        // 变化策略
        rootLayout.setVgrow(headerMenu, Priority.NEVER);
        rootLayout.setVgrow(contentBody, Priority.ALWAYS);
        rootLayout.setVgrow(bottomPane, Priority.NEVER);
        //
        jsonResult = JsonResult.getInstance();
        recentExecutorService = new RecentExecutorService(recentLoadingFun);
        /* 填充最近记录 */
        recentExecutorService.fillRecentMenu(menuRecentBtn);
        // 默认禁止清除
        rightActionBtn.setDisable(true);
        // 资源来源
        jsonResult.resourceProperty().bindBidirectional(fileType.textProperty());
        // ----------------------------------------------------------------------------->
        /* ::RichTextFX::输入框件值变化 */
        SourceAreaHelper.initializeEditor(leftSource, (String changeBal)->{
            if(null==changeBal){
                strCountLabel.setText("0");
            } else {
                strCountLabel.setText(changeBal.length() + "");
            }
        });
        // ::RichTextFX::绑定右键菜单::左侧
        SourceAreaHelper.buildFullContextMenu(leftSource, recentExecutorService, (String content)->{
            // 运行格式化
            this.leftSourceProcesser(content);
        }, (String content)->{
            // 运行保存
            this.saveConentInFile(content);
        }, (Void ins)->{
            getCurrentStage().close();
        });
        // ----------------------------------------------------------------------------->
        // 统一进到jsonResult后由单独的线程去处理@20230329
        jsonResult.stream().successionEnds(Duration.ofMillis(500)).subscribe((Change<String> stringChange)-> {
            this.fillLeftViewContent(stringChange.getNewValue());
        });
        // 置空
        strCountLabel.setStyle("");
        // ComboBox fontSize
        fontSizeSelect.getItems().addAll("12", "14", "16", "18", "20", "22", "24", "26", "28", "30");
        // ComboBox fontFamily
        fontFamilySelect.getItems().addAll(Font.getFamilies());
        // 初始化右侧的代码区
        editorHelper = new CodeAreaHelper();
        editorHelper.initializeEditor(rightView, richEditorContent);
        // https://www.coder.work/article/7722352
        contentBody.getDividers().get(0).positionProperty().addListener((o, oldPos, newPos) -> {
            double leftPos =  1.0f - newPos.doubleValue();
            // System.out.println("width size:"+rootLayout.getWidth()+", right width:"+leftPos* rootLayout.getWidth());
            // 给滚动条留出位置(10)
            rightView.setPrefWidth((leftPos) * rootLayout.getWidth() - 10.0);
        });
        // ----------------------------------------------------------------------------->

    }

    private void fillLeftViewContent(String content){
        if(isBlankContent.test(content)){
            breakOperate("当前资源不存在Json内容");
            return;
        }
        // @20230507
        // System.out.println("[JC]SCR start build");
        SourceContentRender sourceContentRender = new SourceContentRender(content, leftPane);// 开始异步渲染
        //
        // System.out.println("[JC]SCR disable format button");
        this.leftActionBtn.setDisable(true);
        // 使用EventStream@java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-7
        // System.out.println("[JC]SCR subscribe loaded event");
        sourceContentRender.stream().subscribe((Change<Boolean> loadedChange)->{
            if(null==loadedChange.getNewValue()){
                this.leftActionBtn.setDisable(false);
            } else {
                // true加载完了.false正在加载
                this.leftActionBtn.setDisable(!loadedChange.getNewValue());
            }
        });
        sourceContentRender.render();
    }

    // 右侧的按钮:清除
    @FXML
    protected void rightAction(ActionEvent event){
        rightView.clear();
        // 一清空统计没有存在价值了
        jsonTypeGroup.setText("");
        // 绑定
        // 重置左侧的格式桉钮
        this.resetFormatBtnStatus("clear", true);
    }
    // 左侧的按钮:格式化
    @FXML
    protected void leftAction(ActionEvent event){
        String leftTxStr = leftSource.getText();
        /* 左侧没有提示@20230404 */
        // 移进：leftSourceProcesser
        // @20230305-3 取消地址的处理
        this.leftSourceProcesser(leftTxStr);
    }
    private void leftSourceProcesser(String leftTxStr){
        // System.out.println("[RL]left content pretty json");
        if(isBlankContent.test(leftTxStr)){
            breakOperate("当前无内容需要格式化");
            return;
        }
        // @20230507
        // 状态设置
        leftActionBtn.setDisable(true);
        rightActionBtn.setDisable(false);
        //
        try {
            // ------------------------------------------------------>Gson
            JsonElement jvIns = Core.parseJson(leftTxStr);
            // RichCodeArea
            richEditorContent.set(new GsonBuilder().setPrettyPrinting().create().toJson(jvIns));
            rightView.replaceText(0, 0, richEditorContent.get());
            //
            Map<JsonNodeValueType,Integer> typeGroupSize = new JsonNodeTreeGsonBuilder().groupValueType(jvIns);
            jsonTypeGroup.setText(typeGroupSize.entrySet().stream().map(entry->entry.getKey().getSymbol()+": "+entry.getValue()).collect(Collectors.joining("   ")));
        }catch (Exception e){
            // System.out.println("[RL]left content pretty json, has exception:"+e.getMessage());
            Alert a = new Alert(Alert.AlertType.ERROR);
            a.setContentText("格式化失败!");
            // 等待结果
            Optional<ButtonType> result = a.showAndWait();
            // System.out.println("Alter sw result:"+result.isPresent()+", value:"+result.get());
            if (result.isPresent() && result.get() == ButtonType.OK) {
                this.resetFormatBtnStatus("format", true);
            }
        }
    }
    // 左侧TextArea
    @FXML
    protected void sourceInEvent(KeyEvent event){
        if(leftSource.getText().isEmpty()){
            this.resetFormatBtnStatus("keypress", true);
        }else{
            // System.out.println("[RL]left textarea key event");
        }
    }
    private void resetFormatBtnStatus(String refPointer,boolean clearLeftTx){
        // System.out.println("trigger exec pointer:"+refPointer);
        if(clearLeftTx) {
            leftActionBtn.setDisable(false);
            rightActionBtn.setDisable(true);
            // 清空内容
            leftSource.clear();
        }
    }
    // 拖动文件到leftTextArea
    @FXML
    protected void dragJsonFileDropAction(DragEvent event){
        final Dragboard db = event.getDragboard();
        File jsonFile = db.getFiles().get(0);
        final boolean isAccepted = jsonFile.getName().toLowerCase().endsWith(".json");

        if (db.hasFiles() && isAccepted) {
            // display the image file
            // System.out.println("[RL][Drop]drag drop file path:"+jsonFile.getPath());
            loadJsonFile(jsonFile);
        }
        event.consume();

    }
    private void loadJsonFile(File file){
        RemoteForm remoteForm = new RemoteForm();
        remoteForm.setFilePath(file.getPath());
        // 记录请求记录@20230402
        this.recentExecutorService.cache(new RecentRecord(file.getPath(), ResourceType.FILE));
        // 异步
        JsonFileService service = new JsonFileService();
        service.setRemoteForm(remoteForm);
        service.setOnSucceeded((WorkerStateEvent t)-> {
            String leftTxStr = t.getSource().getValue().toString();
            // 统一进到jsonResult@20230329
            jsonResult.setResult(leftTxStr);
            jsonResult.setResource(ResourceType.FILE.getNames());
        });
        service.start();
    }
    @FXML
    private void dragJsonFileOverAction(DragEvent e) {
        final Dragboard db = e.getDragboard();
        // System.out.println("[RL][Drop]drag over file:"+db.getFiles().size());
        final boolean isAccepted = db.getFiles().get(0).getName().toLowerCase().endsWith(".png");
        if (db.hasFiles() && isAccepted) {
                leftPane.setStyle("-fx-border-color: red;"
                        + "-fx-border-width: 5;"
                        + "-fx-border-style: solid;");
                e.acceptTransferModes(TransferMode.COPY);
        }
        e.consume();

    }
    @FXML
    private void dragJsonFileFinishAction(DragEvent e){
        // System.out.println("[RL][Drop]drag Exit");
        leftSource.setStyle("-fx-border-color: #fff;");
        e.consume();
    }
    // 选择字号
    @FXML
    protected void chooseFontSize(ActionEvent event){
        // System.out.println("fontSize:"+fontSizeSelect.getValue());
        Object ffStr = Optional.<Object>ofNullable(fontFamilySelect.getValue()).orElse("System Regular");
        editorHelper.setCodeAreaFont(rightView, ffStr.toString(), Double.valueOf(fontSizeSelect.getValue().toString()));
    }
    // 选择字体
    @FXML
    protected void chooseFontFamily(ActionEvent event){
        // System.out.println("fontFamily:"+fontFamilySelect.getValue());
        Object fsStr = Optional.ofNullable(fontSizeSelect.getValue()).orElse("12");
        editorHelper.setCodeAreaFont(rightView, fontFamilySelect.getValue().toString(), Double.valueOf(fsStr.toString()));
    }
    // 选择颜色
    @FXML
    protected void chooseFontColor(ActionEvent event){
        Color c = fontColorSelect.getValue();
        // System.out.println("fontColorCode:"+c.hashCode());
        String s = Core.toRGBCode(c); // Integer.toHexString(c.hashCode()).substring(0,6).toUpperCase();(65535)
        // System.out.println("fontColor: "+s);
        rightView.setStyle("-fx-text-fill: "+s);
    }
    private Stage getCurrentStage(){
        return (Stage) rootLayout.getScene().getWindow();
    }
    // 菜单的相关事件
    // CTRL+O打开外部资源表单
    @FXML
    protected void openResourceAction(ActionEvent event) throws IOException {
        this.openJsonResourceCTL();
    }
    // CTRL+S保存右侧的格式化内容
    @FXML
    protected void saveAsAction(ActionEvent event) throws IOException {
        this.saveFormatJsonResult();
    }
    // CTRL+V打开Tree视图
    @FXML
    protected void openViewAction(ActionEvent event) throws IOException {
        this.openJsonTreeViewCTL();
    }
    // CTRL+E退出系统
    @FXML
    public void existAction(ActionEvent event){
        // System.out.println("[JC]File Menu close app");
        // @20230312-esb-ok
        getCurrentStage().close();
    }
    public void closeMainController(){
        // 关闭执行器
        this.editorHelper.stop();
        // 将内存中的最近记录持久化
        // 关闭窗口?文件退出?退出进程?
        this.recentExecutorService.persiste();
    }
    // --------------------------------------------->
    // 保存窗口
    private void saveTextToFile(String content, File file) throws IOException{
        try (PrintWriter writer= new PrintWriter(file)) {
            writer.println(content);
        }
    }
    // 打开资源加载窗口
    public void openJsonResourceCTL() throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("yalla-remote.fxml"));
        Stage stage = new Stage();
        Scene scene = new Scene(root, 640, 410); // 放大窗口的高度@20230305-1
        stage.setTitle("请求JSON资源内容");
        stage.setScene(scene);
        // 禁止缩放
        stage.setResizable(false);
        // 取消缩小,放大
        stage.initStyle(StageStyle.UTILITY);
        // stage.setAlwaysOnTop(true);
        stage.show();
    }
    // 保存格式化结果
    public void saveFormatJsonResult() {
        // 右侧有.优先保存右侧. 右侧没有看左侧. 左右都没有提示@20230404
        final String content = Core.getNotEmptyVal(rightView.getText()).orElseGet(()->leftSource.getText());
        /* */
        // 运行保存
        Platform.runLater(()->{
            this.saveConentInFile(content);
        });
    }
    private void breakOperate(String errMessage){
        Alert noConTip = new Alert(Alert.AlertType.WARNING, errMessage, ButtonType.OK);
        Stage stage = (Stage) noConTip.getDialogPane().getScene().getWindow();
        stage.setAlwaysOnTop(true);
        stage.toFront(); // not sure if necessary
        // noConTip.setContentText(errMessage);
        noConTip.show();
    }

    // 保存内容到文件中
    private void saveConentInFile(String content){
        if(isBlankContent.test(content)){
            breakOperate("不存在需要保存内容!");
            return;
        }
        // @20230507
        FileChooser fileChooser = new FileChooser();
        // Set extension filter for text files
        fileChooser.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("JSON files (*.json)", "*.json"),
                new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt")
        );
        // Show save file dialog
        File file = fileChooser.showSaveDialog(getCurrentStage());
        if (file != null) {
            try {
                saveTextToFile(content, file);
            }catch (IOException e){
                Alert noConTip = new Alert(Alert.AlertType.ERROR);
                noConTip.setContentText("保存内容失败!");
                noConTip.show();
            }
        }
    }
    // 打开视图窗口
    public void openJsonTreeViewCTL() throws IOException {
        // 右侧有.优先参考右侧. 右侧没有看左侧. 左右都没有提示@20230404
        String content = Core.getNotEmptyVal(rightView.getText()).orElseGet(()->leftSource.getText());
        if(isBlankContent.test(content)){
            breakOperate("缺少视图数据源!");
            return;
        }
        // @20230507
        // 在Tree视图前保证JsonResult一定有可用值
        if(jsonResult.isBlank()){
            jsonResult.setResult(content);
            jsonResult.setResource(ResourceType.TEXT.getNames());
        }
        //
        Parent root = FXMLLoader.load(getClass().getResource("yalla-tree.fxml"));
        Stage stage = new Stage();
        Scene scene = new Scene(root, 720, 480); // 放大窗口的高度@20230305-1
        stage.setTitle("JSON Tree 视图");
        stage.setScene(scene);
        // 最小宽度
        stage.setMinHeight(520);
        stage.setMinWidth(721);
        // 取消缩小,放大
        // stage.initStyle(StageStyle.UTILITY);
        // stage.setAlwaysOnTop(true);
        stage.show();
    }
    /* 测试@BallProgress*/
    @FXML
    public void ballShowAction() throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("yalla-ball.fxml"));
        Stage stage = new Stage();
        Scene scene = new Scene(root, 720, 480); // 放大窗口的高度@20230305-1
        stage.setTitle("Ball Progressor 视图");
        stage.setScene(scene);
        // 最小宽度
        stage.setMinHeight(920);
        stage.setMinWidth(721);
        stage.show();
    }
    // 新窗口 @ 单例结果集导致Bug
    @FXML
    public void windowShowAction() throws IOException{
        // new JsonMainWindow(new Stage()).run();
        Platform.runLater(()->{
            try {
                new JsonMainWindow(new Stage()).open();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }
}